use crypto_box::{ChaChaBox, PublicKey, SecretKey};
use rand::{rngs::OsRng, RngCore};

pub fn generate_keypair() -> (PublicKey, SecretKey) {
    let mut rng = OsRng::default();

    let private_key = SecretKey::generate(&mut rng);
    let public_key = PublicKey::from(&private_key);

    (public_key, private_key)
}

pub fn generate_nonce() -> [u8; 24] {
    let mut nonce = [0u8; 24];
    OsRng.fill_bytes(&mut nonce);

    nonce
}

#[cfg(test)]
mod tests {
    use blake2::digest::generic_array::GenericArray;
    use crypto_box::aead::{Aead, Payload};

    use super::*;

    #[test]
    fn generating_keypairs() {
        let (alice_public_key, alice_private_key) = generate_keypair();
        let alice_public_key_ = PublicKey::from(&alice_private_key);

        assert_eq!(alice_public_key_, alice_public_key);
    }

    #[test]
    fn encrypt() {
        let (alice_public_key, alice_private_key) = generate_keypair();
        let (boris_public_key, boris_private_key) = generate_keypair();

        let binding = generate_nonce();
        let nonce = GenericArray::from_slice(&binding);

        let boris_encoded = match ChaChaBox::new(&boris_public_key, &alice_private_key).encrypt(
            nonce,
            Payload {
                msg: b"pizda",
                aad: b"pizda",
            },
        ) {
            Ok(value) => value,
            Err(_) => vec![],
        };

        let boris_encoded = hex::encode(boris_encoded);

        let alice_encoded = match ChaChaBox::new(&alice_public_key, &boris_private_key).encrypt(
            nonce,
            Payload {
                msg: b"pizda",
                aad: b"pizda",
            },
        ) {
            Ok(value) => value,
            Err(_) => vec![],
        };

        let alice_encoded = hex::encode(alice_encoded);

        println!("{boris_encoded} {alice_encoded}");

        assert!(alice_encoded == boris_encoded);
    }

    #[test]
    fn decrypt() {
        let (alice_public_key, alice_private_key) = generate_keypair();
        let (boris_public_key, boris_private_key) = generate_keypair();

        let binding = generate_nonce();
        let nonce = GenericArray::from_slice(&binding);

        let alice_encoded = match ChaChaBox::new(&boris_public_key, &alice_private_key).encrypt(
            nonce,
            Payload {
                msg: b"pizda",
                aad: b"pizda",
            },
        ) {
            Ok(value) => value,
            Err(_) => vec![],
        };

        let alice_encoded = hex::encode(alice_encoded);

        let boris_encoded = match ChaChaBox::new(&alice_public_key, &boris_private_key).encrypt(
            nonce,
            Payload {
                msg: b"pizda",
                aad: b"pizda",
            },
        ) {
            Ok(value) => value,
            Err(_) => vec![],
        };

        let boris_encoded = hex::encode(boris_encoded);

        let boris_decoded = match ChaChaBox::new(&alice_public_key, &boris_private_key).decrypt(
            nonce,
            Payload {
                msg: &hex::decode(boris_encoded).unwrap(),
                aad: b"pizda",
            },
        ) {
            Ok(encoded) => encoded,
            Err(_) => vec![],
        };

        let boris_decoded = std::str::from_utf8(&boris_decoded).unwrap();

        let alice_decoded = match ChaChaBox::new(&boris_public_key, &alice_private_key).decrypt(
            nonce,
            Payload {
                msg: &hex::decode(alice_encoded).unwrap(),
                aad: b"pizda",
            },
        ) {
            Ok(encoded) => encoded,
            Err(_) => vec![],
        };

        let alice_decoded = std::str::from_utf8(&alice_decoded).unwrap();

        println!("{boris_decoded} {alice_decoded}");

        assert!(alice_decoded == boris_decoded);
    }
}
